package com.feilong.commons.core.date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 阴历日期,农历日期
 * 
 * @author <a href="mailto:venusdrogon@163.com">金鑫</a>
 * @version 1.0 2010-2-8 下午04:59:32
 * @since 1.0
 */
public final class LunarDateUtil{

	private final static Logger	log	= LoggerFactory.getLogger(LunarDateUtil.class);

	public int					year;

	public int					month;

	public int					day;

	public LunarDateUtil(int year, int month, int day){
		this.year = year;
		this.month = month;
		this.day = day;
	}

	/**
	 * 农历转成阳历The lunar calendar is turned into the Solar calendar
	 * 
	 * @param year
	 *            农历年份
	 * @param month
	 *            农历月份
	 * @param day
	 *            农历日期
	 * @return 阳历
	 */
	public static String convertLundarCalendarToSolar(int year,int month,int day){
		int iSYear;
		int iSMonth;
		int iSDay;
		int iOffsetDays = LunarDateUtil.iGetLNewYearOffsetDays(year, month, day) + LunarDateUtil.solarLunarOffsetTable[year - 1901];
		int iYearDays = DateUtil.isLeapYear(year) ? 366 : 365;
		if (iOffsetDays >= iYearDays){
			iSYear = year + 1;
			iOffsetDays -= iYearDays;
		}else{
			iSYear = year;
		}
		iSDay = iOffsetDays + 1;
		for (iSMonth = 1; iOffsetDays >= 0; ++iSMonth){
			iSDay = iOffsetDays + 1;
			iOffsetDays -= CalendarUtil.getMaxDayInMonth(iSYear, iSMonth);
		}
		iSMonth--;
		return "" + iSYear + (iSMonth > 9 ? iSMonth + "" : "0" + iSMonth) + (iSDay > 9 ? iSDay + "" : "0" + iSDay);
	}

	/**
	 * 阳历年月日获得对应的阴历
	 * 
	 * @param year
	 *            阳历年
	 * @param month
	 *            阳历月
	 * @param day
	 *            阳历日
	 * @return 阳历年月日获得对应的阴历
	 */
	public static String getLunarDateString(int year,int month,int day){
		int iDate = Integer.parseInt(SolarDateUtil.convertSolarCalendarToLundar(year, month, day));
		int year_Lunar = iDate / 10000;
		int month_Lunar = iDate % 10000 / 100;
		int day_Lunar = iDate % 100;
		LunarDateUtil lunarDate = new LunarDateUtil(year_Lunar, month_Lunar, day_Lunar);
		return lunarDate.getLunarDateString();
	}

	// Days in this year of lunar calendar
	public static int iGetLYearDays(int iYear){
		int iYearDays = 0;
		int iLeapMonth = getLeapMonth(iYear);
		for (int i = 1; i < 13; ++i){
			iYearDays += iGetLMonthDays(iYear, i);
		}
		if (iLeapMonth > 0){
			iYearDays += iGetLMonthDays(iYear, iLeapMonth + 12);
		}
		return iYearDays;
	}

	public static int iGetLNewYearOffsetDays(int iYear,int iMonth,int iDay){
		int iOffsetDays = 0;
		int iLeapMonth = getLeapMonth(iYear);
		if ((iLeapMonth > 0) && (iLeapMonth == iMonth - 12)){
			iMonth = iLeapMonth;
			iOffsetDays += iGetLMonthDays(iYear, iMonth);
		}
		for (int i = 1; i < iMonth; ++i){
			iOffsetDays += iGetLMonthDays(iYear, i);
			if (i == iLeapMonth){
				iOffsetDays += iGetLMonthDays(iYear, iLeapMonth + 12);
			}
		}
		iOffsetDays += iDay - 1;
		return iOffsetDays;
	}

	/**
	 * 获得某年某月农历最大的天数
	 * 
	 * @param iYear
	 *            年
	 * @param iMonth
	 *            农历月
	 * @return 获得某年某月农历最大的天数
	 */
	public static int iGetLMonthDays(int iYear,int iMonth){
		int iLeapMonth = getLeapMonth(iYear);
		if ((iMonth > 12) && (iMonth - 12 != iLeapMonth) || (iMonth < 0)){
			log.error("Wrong month, ^_^ , i think you are want a -1, go to death!");
			return -1;
		}
		if (iMonth - 12 == iLeapMonth){
			if ((LunarDateUtil.lunarMonthDaysTable[iYear - 1901] & (0x8000 >> iLeapMonth)) == 0){
				return 29;
			}
			return 30;
		}
		if ((iLeapMonth > 0) && (iMonth > iLeapMonth)){
			iMonth++;
		}
		if ((LunarDateUtil.lunarMonthDaysTable[iYear - 1901] & (0x8000 >> (iMonth - 1))) == 0){
			return 29;
		}
		return 30;
	}

	/**
	 * 获得闰月
	 * 
	 * @param year
	 * @return
	 */
	public static int getLeapMonth(int year){
		char iMonth = LunarDateUtil.lunarLeapMonthTable[(year - 1901) / 2];
		if (year % 2 == 0){
			return (iMonth & 0x0f);
		}
		return (iMonth & 0xf0) >> 4;
	}

	// *************************************************************************
	/**
	 * 获得农历字符串
	 * 
	 * @return 农历字符串
	 */
	public String getLunarDateString(){
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append("农历:");
		stringBuilder.append(CalendarUtil.convertYearToChineseYear(year));
		stringBuilder.append("(" + CalendarUtil.getChineseGanZhi(year) + ")");
		stringBuilder.append("年");
		if (month > 12){
			month -= 12;
			stringBuilder.append("闰");
		}
		if (month == 12){
			stringBuilder.append("腊月");
		}else if (month == 11){
			stringBuilder.append("冬月");
		}else if (month == 1){
			stringBuilder.append("正月");
		}else{
			stringBuilder.append(CalendarUtil.chinses_numbers[month] + "月");
		}
		if (day > 29){
			stringBuilder.append("三十");
		}else if (day > 20){
			stringBuilder.append("二十" + CalendarUtil.chinses_numbers[day % 20]);
		}else if (day == 20){
			stringBuilder.append("二十");
		}else if (day > 10){
			stringBuilder.append("十" + CalendarUtil.chinses_numbers[day % 10]);
		}else{
			stringBuilder.append("初" + CalendarUtil.chinses_numbers[day]);
		}
		return stringBuilder.toString();
	}

	/**
	 * 获得农历星期
	 * 
	 * @return 获得农历星期
	 */
	public String getChineseWeek(){
		int iOffsetDays = 0;
		for (int i = 1901; i < year; ++i){
			iOffsetDays += iGetLYearDays(i);
		}
		iOffsetDays += iGetLNewYearOffsetDays(year, month, day);
		return CalendarUtil.getChineseWeek((iOffsetDays + 2) % 7);
	}

	/**
	 * 阴历转成阳历
	 * 
	 * @return SolarDateUtil
	 */
	public SolarDateUtil toSolarDate(){
		int iYear, iMonth, iDay, iDate;
		iDate = Integer.parseInt(convertLundarCalendarToSolar(this.year, this.month, this.day));
		iYear = iDate / 10000;
		iMonth = iDate % 10000 / 100;
		iDay = iDate % 100;
		SolarDateUtil solarDate = new SolarDateUtil(iYear, iMonth, iDay);
		return solarDate;
	}

	/**
	 * 农历闰月表 1901-2050
	 * 
	 * <pre>
	 *   Array iLunarLeapMonthTable preserves the lunar calendar leap month from 1901 to 2050,
	 * 	  if it is 0 express not to have , every byte was stored for two years
	 * </pre>
	 */
	public static final char[]	lunarLeapMonthTable		= { 0x00, 0x50, 0x04, 0x00, 0x20, // 1910
			0x60,
			0x05,
			0x00,
			0x20,
			0x70, // 1920
			0x05,
			0x00,
			0x40,
			0x02,
			0x06, // 1930
			0x00,
			0x50,
			0x03,
			0x07,
			0x00, // 1940
			0x60,
			0x04,
			0x00,
			0x20,
			0x70, // 1950
			0x05,
			0x00,
			0x30,
			0x80,
			0x06, // 1960
			0x00,
			0x40,
			0x03,
			0x07,
			0x00, // 1970
			0x50,
			0x04,
			0x08,
			0x00,
			0x60, // 1980
			0x04,
			0x0a,
			0x00,
			0x60,
			0x05, // 1990
			0x00,
			0x30,
			0x80,
			0x05,
			0x00, // 2000
			0x40,
			0x02,
			0x07,
			0x00,
			0x50, // 2010
			0x04,
			0x09,
			0x00,
			0x60,
			0x04, // 2020
			0x00,
			0x20,
			0x60,
			0x05,
			0x00, // 2030
			0x30,
			0xb0,
			0x06,
			0x00,
			0x50, // 2040
			0x02,
			0x07,
			0x00,
			0x50,
			0x03										// 2050
														};

	/**
	 * 1901-2050每年阴历和农历相差量
	 * 
	 * <pre>
	 * // Array iSolarLunarTable stored the offset days
	 * // in New Year of solar calendar and lunar calendar from 1901 to 2050;
	 * </pre>
	 */
	public static final char[]	solarLunarOffsetTable	= { 49, 38, 28, 46, 34, 24, 43, 32, 21, 40, // 1910
			29,
			48,
			36,
			25,
			44,
			34,
			22,
			41,
			31,
			50, // 1920
			38,
			27,
			46,
			35,
			23,
			43,
			32,
			22,
			40,
			29, // 1930
			47,
			36,
			25,
			44,
			34,
			23,
			41,
			30,
			49,
			38, // 1940
			26,
			45,
			35,
			24,
			43,
			32,
			21,
			40,
			28,
			47, // 1950
			36,
			26,
			44,
			33,
			23,
			42,
			30,
			48,
			38,
			27, // 1960
			45,
			35,
			24,
			43,
			32,
			20,
			39,
			29,
			47,
			36, // 1970
			26,
			45,
			33,
			22,
			41,
			30,
			48,
			37,
			27,
			46, // 1980
			35,
			24,
			43,
			32,
			50,
			39,
			28,
			47,
			36,
			26, // 1990
			45,
			34,
			22,
			40,
			30,
			49,
			37,
			27,
			46,
			35, // 2000
			23,
			42,
			31,
			21,
			39,
			28,
			48,
			37,
			25,
			44, // 2010
			33,
			23,
			41,
			31,
			50,
			39,
			28,
			47,
			35,
			24, // 2020
			42,
			30,
			21,
			40,
			28,
			47,
			36,
			25,
			43,
			33, // 2030
			22,
			41,
			30,
			49,
			37,
			26,
			44,
			33,
			23,
			42, // 2040
			31,
			21,
			40,
			29,
			47,
			36,
			25,
			44,
			32,
			22, // 2050
														};

	/**
	 * 农历日期表 1901-2100农历表
	 * 
	 * <pre>
	 *  Array lIntLunarDay is stored in the monthly day information in every year from 1901 to 2100 of the lunar calendar,
	 *  农历只能每个月29天或者30天,1年中有12或者13个月
	 * 	  The lunar calendar can only be 29 or 30 days every month, express with 12(or 13) pieces of binary bit in one year,
	 * 	  it is 30 days for 1 form in the corresponding location , otherwise it is 29 days
	 * </pre>
	 */
	public static final int[]	lunarMonthDaysTable		= { 0x4ae0, 0xa570, 0x5268, 0xd260, 0xd950, 0x6aa8, 0x56a0, 0x9ad0, 0x4ae8, 0x4ae0, // 1910
			0xa4d8,
			0xa4d0,
			0xd250,
			0xd548,
			0xb550,
			0x56a0,
			0x96d0,
			0x95b0,
			0x49b8,
			0x49b0, // 1920
			0xa4b0,
			0xb258,
			0x6a50,
			0x6d40,
			0xada8,
			0x2b60,
			0x9570,
			0x4978,
			0x4970,
			0x64b0, // 1930
			0xd4a0,
			0xea50,
			0x6d48,
			0x5ad0,
			0x2b60,
			0x9370,
			0x92e0,
			0xc968,
			0xc950,
			0xd4a0, // 1940
			0xda50,
			0xb550,
			0x56a0,
			0xaad8,
			0x25d0,
			0x92d0,
			0xc958,
			0xa950,
			0xb4a8,
			0x6ca0, // 1950
			0xb550,
			0x55a8,
			0x4da0,
			0xa5b0,
			0x52b8,
			0x52b0,
			0xa950,
			0xe950,
			0x6aa0,
			0xad50, // 1960
			0xab50,
			0x4b60,
			0xa570,
			0xa570,
			0x5260,
			0xe930,
			0xd950,
			0x5aa8,
			0x56a0,
			0x96d0, // 1970
			0x4ae8,
			0x4ad0,
			0xa4d0,
			0xd268,
			0xd250,
			0xd528,
			0xb540,
			0xb6a0,
			0x96d0,
			0x95b0, // 1980
			0x49b0,
			0xa4b8,
			0xa4b0,
			0xb258,
			0x6a50,
			0x6d40,
			0xada0,
			0xab60,
			0x9370,
			0x4978, // 1990
			0x4970,
			0x64b0,
			0x6a50,
			0xea50,
			0x6b28,
			0x5ac0,
			0xab60,
			0x9368,
			0x92e0,
			0xc960, // 2000
			0xd4a8,
			0xd4a0,
			0xda50,
			0x5aa8,
			0x56a0,
			0xaad8,
			0x25d0,
			0x92d0,
			0xc958,
			0xa950, // 2010
			0xb4a0,
			0xb550,
			0xb550,
			0x55a8,
			0x4ba0,
			0xa5b0,
			0x52b8,
			0x52b0,
			0xa930,
			0x74a8, // 2020
			0x6aa0,
			0xad50,
			0x4da8,
			0x4b60,
			0x9570,
			0xa4e0,
			0xd260,
			0xe930,
			0xd530,
			0x5aa0, // 2030
			0x6b50,
			0x96d0,
			0x4ae8,
			0x4ad0,
			0xa4d0,
			0xd258,
			0xd250,
			0xd520,
			0xdaa0,
			0xb5a0, // 2040
			0x56d0,
			0x4ad8,
			0x49b0,
			0xa4b8,
			0xa4b0,
			0xaa50,
			0xb528,
			0x6d20,
			0xada0,
			0x55b0										// 2050
														};
}